/**
 * 用于解析 html 模板，生成 AST
 */

/**
 * 从 Vue2 源码中抄过来的正则
 * @type {RegExp}
 */
// 匹配属性对，比如 name="gengwenhao" name='gengwenhao' name=gengwenhao
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
// 匹配标签名，比如 <HomePage></HomePage> <home-page></home-page>
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`
// 匹配带冒号的标签名，比如 <geng:header></geng:header>
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
// 匹配标签开始，比如 <div
const startTagOpen = new RegExp(`^<${qnameCapture}`)
// 匹配标签闭合，比如 > />
const startTagClose = /^\s*(\/?)>/
// 匹配结束标签，比如 </div>
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)

// 将 HTML 转换为 AST
export function parseHTML(html) {
  // 根节点
  let root
  // 文本内容
  let text = ''
  // 父节点
  let currentParent
  // 字符栈
  const stack = []

  while (html) {
    let textEnd = html.indexOf('<')

    // 如果匹配到 <
    if (textEnd === 0) {
      // 匹配到开始标签
      const startTagMatch = parseStartTag()
      if (startTagMatch) {
        handleStartTag(startTagMatch)
        continue
      }

      // 匹配到结束标签
      const endTagMatch = html.match(endTag)
      if (endTagMatch) {
        advance(endTagMatch[0].length)
        handleEndTag(endTagMatch)
        continue
      }
    }

    // 如果匹配到文字
    if (textEnd > 0) {
      text = html.slice(0, textEnd)
    }

    if (text) {
      // 向前走
      advance(text.length)
      handleText(text)
    }
  }

  // 解析开始标签
  function parseStartTag() {
    // 匹配到的开始标签的格式 ['<div', 'div', index: 0, ...]
    const start = html.match(startTagOpen)

    // 定义匹配标志位
    let end
    let attrs

    // 如果匹配到了 开始标签
    if (start) {
      // 构造对象
      const match = {
        tagName: start[1],
        attrs: []
      }

      // 向前走
      advance(start[0].length)

      // 如果没匹配到 标签闭合 且匹配到了 属性对
      while (!(end = html.match(startTagClose)) && (attrs = html.match(attribute))) {
        // 匹配到的 attrs 的格式
        // [" id="app", "id", "=", "app", null, null]

        // 装入属性对
        match.attrs.push({
          name: attrs[1],
          value: attrs[3] || attrs[4] || attrs[5]
        })

        // 向前走
        advance(attrs[0].length)
      }

      // 如果匹配到了 标签闭合
      if (end) {
        // 向前走
        advance(end[0].length)

        return match
      }
    }

  }

  // 处理开始标签
  function handleStartTag(match) {
    const {tagName, attrs} = match
    const element = createASTElement(tagName, attrs)

    if (!root) {
      root = element
    }

    currentParent = element
    stack.push(element)
  }

  // 处理结束标签
  function handleEndTag(match) {
    const element = stack.pop()
    currentParent = stack[stack.length - 1]
    if (currentParent) {
      element.parent = currentParent
      currentParent.children.push(element)
    }
  }

  function handleText(text) {
    (text = text.trim()) && currentParent.children.push({type: 3, text})
  }

  // 截断 HTML
  function advance(n) {
    html = html.slice(n)
  }

  return root
}

function createASTElement(tagName, attrs) {

  return {
    tag: tagName,
    type: 1,
    children: [],
    attrs,
    parent
  }
}


